package jmpo.ex;

import java.util.Scanner;

/**
 * Исключения можно не только ловить, но и выбрасывать.
 * Для этого используется ключевое слово throw.
 * Ниже представлен пример метода, который выбрасывает 
 * IllegalArgumentException при недопустимых входящих параметрах.
 */
final class Triangle {
    Triangle(long a, long b, long c) {
        /**
         * Мы проверяем неравество треугольника и 
         * выбрасываем исключение о неверных входящих 
         * параметрах.
         */
        if (!(a + b > c && a + c > b && b + c > a))
            throw new IllegalArgumentException("Triangle inequality is failed!");
        this.a = a;
        this.b = b;
        this.c = c;
    }

    private long a, b, c;

    public static void test() {
        long a = 10, b = 7, c = 2;
        try {
            var t = new Triangle(a, b, c);
        } catch (IllegalArgumentException e) {
            System.err.println(e.getMessage());
        }
    }
}

/**
 * Мы можем использовать не только стандартные исключения, но и свои собственные.
 * Для этого мы расширяем класс RuntimeException.
 * !!!
 * Заметим, что язык не запрещает нам создавать исключения, которые напрямую
 * наследуют Throwable и Exception. Но мы предерживаемся определённой конвенции.
 * !!!
 */
final class MyException extends RuntimeException {
    /**
     * Даже такой пустой класс, который не содержит
     * данных может быть полезен, так как он своим 
     * типом сообщает о некоторой ошибке.
     * Конечно более разумным было бы сохранить некоторую
     * специфическую инфомацию об этом исключении.
     */
}

final class WrongSegmentException extends RuntimeException {
    WrongSegmentException(long begin, long end, String message) {
        super(message); // передаём строку сообщения в родительский класс
        this.begin = begin;
        this.end = end;
    }

    long getBegin() { return this.begin; }
    long getEnd() { return this.end; }

    private long begin, end;
}

/**
 * Иногда нам нужно преобразовать одни тип исключения в другой, 
 * чтобы сопрягать различные слои архитектуры или библиотеки.
 * В этой ситуации мы выбрасываем новое исключение, завернув 
 * в него предыдущее. 
 */
final class Segment {
    Segment(long begin, long end) {
        if (!(begin <= end))
            throw new WrongSegmentException(begin, end, "Begin must be lower or equal to the End");
    }

    private long begin, end;

    public static void test() {
        try {
            try {
                var sc = new Scanner("10 5");
                var begin = sc.nextLong();
                var end = sc.nextLong();
                var s = new Segment(begin, end);
            } catch(WrongSegmentException e) {
                throw new RuntimeException("Segment exception", e);
            }
        } catch(Exception e) {
            System.err.println(e.getMessage());
            System.err.println(e.getCause());
            var ws = (WrongSegmentException)e.getCause();
            System.err.format("beg: %d, end: %d", ws.getBegin(), ws.getEnd());
        }
    }
}

public class ExceptionThrowing {
    public static void main(String[] args) {
        if (false) Triangle.test();
        if (false) Segment.test();
    }
}
